package org.sdnplatform.sync.internal.config.bootstrap;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.atomic.AtomicInteger;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.TimeoutException;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timer;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.sdnplatform.sync.error.SyncException;
import org.sdnplatform.sync.internal.SyncManager;
import org.sdnplatform.sync.internal.config.AuthScheme;
import org.sdnplatform.sync.internal.config.Node;
import org.sdnplatform.sync.internal.rpc.RPCService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.net.HostAndPort;
/**
* Makes an attempt to bootstrap the cluster based on seeds stored in the
* local system store
* @author readams
*/
public class BootstrapClient {
protected static final Logger logger =
LoggerFactory.getLogger(BootstrapClient.class);
/**
* Channel group that will hold all our channels
*/
private ChannelGroup cg;
/**
* Transaction ID used in message headers in the RPC protocol
*/
protected AtomicInteger transactionId = new AtomicInteger();
/**
* The {@link SyncManager} that we'll be bootstrapping
*/
protected SyncManager syncManager;
protected final AuthScheme authScheme;
protected final String keyStorePath;
protected final String keyStorePassword;
EventLoopGroup workerExecutor = null;
Bootstrap bootstrap = null;
BootstrapChannelInitializer pipelineFactory;
protected Node localNode;
protected volatile boolean succeeded = false;
private Timer timer;
public BootstrapClient(SyncManager syncManager, AuthScheme authScheme,
String keyStorePath, String keyStorePassword) {
super();
this.syncManager = syncManager;
this.authScheme = authScheme;
this.keyStorePath = keyStorePath;
this.keyStorePassword = keyStorePassword;
}
public void init() throws SyncException {
cg = new DefaultChannelGroup("Cluster Bootstrap", GlobalEventExecutor.INSTANCE);
workerExecutor = new NioEventLoopGroup();
timer = new HashedWheelTimer();
bootstrap = new Bootstrap()
.group(workerExecutor)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.SO_SNDBUF, RPCService.SEND_BUFFER_SIZE)
.option(ChannelOption.SO_RCVBUF, RPCService.SEND_BUFFER_SIZE)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, RPCService.CONNECT_TIMEOUT);
pipelineFactory = new BootstrapChannelInitializer(timer, this);
bootstrap.handler(pipelineFactory);
}
public void shutdown() {
if (cg != null) {
cg.close().awaitUninterruptibly();
cg = null;
}
bootstrap = null;
pipelineFactory = null;
if (workerExecutor != null) {
try {
workerExecutor.shutdownGracefully();
} catch (TimeoutException e) {
logger.warn("Error waiting for gracefull shutdown of BootstrapClient {}", e);
}
workerExecutor = null;
}
if (timer != null) {
timer.stop();
timer = null;
}
}
public boolean bootstrap(HostAndPort seed,
Node localNode) throws SyncException {
this.localNode = localNode;
succeeded = false;
SocketAddress sa =
new InetSocketAddress(seed.getHostText(), seed.getPort());
ChannelFuture future = bootstrap.connect(sa);
future.awaitUninterruptibly();
if (!future.isSuccess()) {
logger.debug("Could not connect to " + seed, future.cause());
return false;
}
Channel channel = future.channel();
logger.debug("[{}] Connected to {}",
localNode != null ? localNode.getNodeId() : null,
seed);
try {
channel.closeFuture().await();
} catch (InterruptedException e) {
logger.debug("Interrupted while waiting for bootstrap");
return succeeded;
}
return succeeded;
}
public ChannelGroup getChannelGroup() {
return cg;
}
}